#!/usr/bin/env python
#-*-coding=utf-8-*-

'''
file : utpreal.py
@author: Timothée CLAIN
@copyright: Timothée CLAIN
@license: GNU GPL V3
'''
from optparse import OptionParser
from shelltools.io import file as ftools
import re,sys
from  types import ListType

def parse(content,regexp,is_file=True,flags=None):
    '''
    parse content and retrieve all the occurences of regexp in the content content 
    param content :  the content to process : can be either a path to a file or arbitrary content
    param regexp : the RE usd in processing content
    param : is_file : notify if content is a file path or not
    param : flags : additional flags passed to RE
    '''
    if is_file:
        fcontent = ftools.read_file(content)
    else :
        fcontent = content
    if flags :
        return re.findall(regexp,fcontent,flags)
    else :
        return re.findall(regexp,fcontent)
    

def parse_template(tpl,is_file=True):
    '''
    Parse the template and retrieve the defines variables
    param : tpl :the content to parse
    param : is_file, notify if the content is a file
    '''
    
    vars = parse(tpl,r"\{([^\{\}:]*):([^\{\}]*)\}{1}?")
    return vars
    
def parse_parameters(parameters,is_file=True):
    '''
    parsing the parameter file provided in the command line
    param : parameters :the content to parse
    param : is_file, notify if the content is a file 
    '''
    if type(parameters) is ListType :
        values = parameters
    else :
        values = parse(parameters,r"([^:]*):(.*)",is_file)
        
    return values
    
def get_parser():
    '''
    define a parser for the argument provided in the command-line
    '''
    parser = OptionParser(usage="""\
UTP - Universal Template Processor
Minimalist Template Engine for non-web files
Format : %prog [options]

Utilisation typique :
    %prog --input=test.tpl --parameter-file=parameter.pf --output="out.ini" 
ou avec les options courtes :
    %prog -i test.tpl -p parameterf.pf -o out.ini  
""")
    parser.add_option('-i','--input',dest='tpl',
                      type='string',action='store',
                      help="""Input template file. Mandatory
                      Variables format : {var_name:default_value}. default_value can be empty but the ':' is mandatory.
                      """)
    parser.add_option('-o','--output',dest='out',
                      type='string',action='append',
                      help="""Out file. If not specified, the stdout is used
                      File Format :
                          var1:value1
                          var2:value2 ...
                      """)
    parser.add_option('-p','--parameter-file',dest='parameters',
                      type='string',action='store',
                      help="""Parameter file of the template 
                      """)
    parser.add_option('-a','--add-parameter',dest='parameters_list',
                      type='string',action='append',
                      help="""Individual Parameters read from command line with this format :
                      -a var1:value1 -a var2 
                      """)
    
    parser.add_option('-l','--lookup',dest='list_variables',
                      action='store_true',
                      help="""List availables variables in template
                      """)
    
    return parser

def read_parameters(varnames=None):
    '''
    reads parameters from the terminal if the argument --parameter-file is not provided
    param : varnames : the available variables 
    '''
    mapping = []
    while True:
        print """Add a mapping : Y(es), n(o)"""
        if re.match(r"[Yy]$", raw_input()):
            print """Type it with this format : variable:value"""
            res = re.match(r"([^:]*):(.*)",raw_input())
            if res:
                mapping.append(res.groups()) 
            else :
                print "Format Error - Discarding"
                continue
        else:
            break
    if len(mapping) == 0 :
        return None
    else :
        return mapping

def get_parameters_from_args(rawparameters):
    '''
    process mappings variable:values from cli 
    '''
    parameters = []
    regexp = re.compile(r"([^:]*):(.*)")
    for param in rawparameters:
        paramparsed = regexp.match(param)
        if paramparsed:
            parameters.append(paramparsed.groups())
        else :
            return False
    return parameters

def process_tpl(tpl,final_vars,out=None):
    '''
    generate the output of a template following the mapping given
    '''
    #TODO : transformer en classe
    tpl_content = ftools.read_file(tpl)
    
    #dictionnary containing a list of regular expressions used
    REs = {}
    for k,v in final_vars.iteritems():
        #"\{([^\{\}]*):([^\{\}]*)\}{1}?"
        REs[k] = r'\{%s:.*\}' % k
    for k,v in REs.iteritems():
        tpl_content = re.sub(v,final_vars[k],tpl_content)
    if not out :
        print tpl_content
    else :
        o = ftools.get_file_handle(out)
        o.write(tpl_content)
        o.close()

def get_unique_mapping(raw_mapping):
    '''
    process a list raw_mapping that contains simple mappings in tuple
    like (var,value1), (var,)
    fusion the mappings and returns a dictionnary
    For instance, 
    '''
    
    vars= {}
    for entry in raw_mapping :
        if entry[0] not in vars :
            vars[entry[0]] = None
    for k,v in vars.iteritems() :
        for entry in raw_mapping :
            if vars[entry[0]] == None :
                vars[entry[0]] = entry[1]
            del entry
    return vars

def map_vars_and_values(rawvars,rawvalues):
    '''
    process a mapping between the array vars and values. Produce a mapping table
    param : rawvars -> information retrieved from template file|stream
    param : rawvalues -> information retrieved from parameter file|stream
    '''
    drawvars = get_unique_mapping(rawvars)
    drawvalues = get_unique_mapping(rawvalues)
    final_mapping = {}
    
    for k,v in drawvars.iteritems():
        if drawvalues.get(k,None):
            final_mapping[k] = drawvalues[k]
        else :
            final_mapping[k] = drawvars[k]
            
    return final_mapping


def utp(tpl,parameters,out=None,list=False,tpl_is_file=True,parameters_is_file=True):
    '''
    main function of the library
    param : tpl : the template to process
    pfile : the parameter
    '''
    #parse the template stream
    tplparsed = parse_template(tpl,tpl_is_file)
    #parse the parameters stream
    parametersparsed = parse_parameters(parameters,parameters_is_file)
    #parse 
    final_vars = map_vars_and_values(tplparsed, parametersparsed)
    process_tpl(tpl, final_vars, out)

def format_variable(var,value):
    '''
    build a formatted form of var
    '''
    return "%s:%s" % (var.ljust(30),value.rjust(30))

def list_variables(tpl):
    '''
    print in stdout, the  list of variables contained in template tpl
    '''
    tplparsed = parse_template(tpl,tpl_is_file)
    print "Available Variables "
    print format_variable("variable", " default value")
    for var in tplparsed : 
        print format_variable(var[0],var[1])
        
def setEncoding(encoding='utf-8'):
    '''
    Définit la sortie en utf-8
    '''
    reload(sys)
    sys.setdefaultencoding(encoding)


if __name__ == '__main__':
    setEncoding()
    
    #parsing args
    parser = get_parser()
    opts, args = parser.parse_args()
    
    tpl = opts.tpl
    #TODO : Handle case where tpl is a stream not a file
    tpl_is_file = True
    if not tpl :
        parser.print_help()
        sys.exit(1)
    list_vars = opts.list_variables 
    
    if list_vars : 
        list_variables(tpl)
        sys.exit(1)
    
    
    out = opts.out
    if not out :
        out = None
    
    parameters_ok = False # False if parameters were not processed
    
    parameters = opts.parameters
    parameters_is_file = True
    
    parameters_list = opts.parameters_list
    if parameters_list and not parameters:
        parameters_list = get_parameters_from_args(parameters_list)
        parameter_is_file = False
        parameters = parameters_list
        
    
    #lauch main function
    utp(tpl,parameters,out,tpl_is_file=tpl_is_file,parameters_is_file=parameters_is_file)
